﻿using Furion;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
using System.Threading;
using System.Threading.Tasks;
using YFurion.Application.MongoDB.Extensions;
using YFurion.Entities;
using YFurion.Extensions;

namespace YFurion.Application
{
    public abstract class MongoDBApplicationServiceBase<TEntity, TEntityDto, TGetAllInput> : ServiceBase
        where TEntity : class, IYEntityBase<ObjectId>, new()
        where TEntityDto : class, IEntityDto<ObjectId>
    {
        protected const string DefaultDBName = "DefaultDB";

        protected IMongoDBRepository MongoDBRepository { get; }

        protected IMongoDatabase Database { get; set; }

        protected IMongoCollection<TEntity> Collections { get; set; }

        protected MongoDBApplicationServiceBase()
        {
            MongoDBRepository = App.GetService<IMongoDBRepository>();
            Init();
        }

        protected virtual void Init()
        {
            string dbName = App.Configuration["MongoDB:DBName"];
            Database = MongoDBRepository.GetDatabase(dbName.IsNullOrWhiteSpace() ? DefaultDBName : dbName);
            Collections = Database.GetCollection<TEntity>(typeof(TEntity).Name);
        }

        protected virtual IMongoQueryable<TEntity> CreateFilteredQuery(TGetAllInput input)
        {
            IMongoQueryable<TEntity> query = Collections.AsQueryable();
            return query;
        }

        /// <summary>
        /// Should apply paging if needed.s
        /// </summary>
        /// <param name="query">The query.</param>
        /// <param name="input">The input.</param>
        protected virtual IMongoQueryable<TEntity> ApplyPaging(IMongoQueryable<TEntity> query, TGetAllInput input)
        {
            //Try to use paging if available
            IPagedResultRequest pagedInput = input as IPagedResultRequest;
            if (pagedInput != null)
            {
                return query.PageBy(pagedInput);
            }

            //Try to limit query result if available
            ILimitedResultRequest limitedInput = input as ILimitedResultRequest;
            if (limitedInput != null)
            {
                return query.Take(limitedInput.MaxResultCount);
            }

            //No paging
            return query;
        }

        #region 调用方法之前调用
        /// <summary>
        /// 在调用查询一条方法之前调用
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        protected virtual async Task OnGetExecuting(TEntity entity, CancellationToken cancellationToken)
        {
            await OnExecuting(entity, cancellationToken);
        }

        /// <summary>
        /// 在调用更新方法之前调用
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        protected virtual async Task OnUpdateExecuting(TEntity entity, CancellationToken cancellationToken)
        {
            await OnExecuting(entity, cancellationToken);
        }

        /// <summary>
        /// 在调用删除方法之前调用
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        protected virtual async Task OnDeleteExecuting(TEntity entity, CancellationToken cancellationToken)
        {
            await OnExecuting(entity, cancellationToken);
        }
        #endregion
    }
}
